
my $sde_total = 2048;
my $r5hash = Sym_SymToAddr("Raid5HashTable");
my %ipkpages = ();
my %scepages = ();

sub analyze_ipk {
  my $ipk_addr = shift;
  return if($ipk_addr==0);

  my $pagetable = Dmp_Iu32AtPpc($ipk_addr+0x2c);
  my $offset = Dmp_Iu8AtPpc($ipk_addr+0x1c);
  my $count = Dmp_Iu8AtPpc($ipk_addr+0x1d);
  
  my $pagetable_addr = Adr_rTableToPpc($pagetable);
  if( $pagetable_addr == 0 ) {
    print "Invalid pagetable\n";
    return;
  }
  for(my $i=0;$i<$count;$i++) {
    my $page = Dmp_Iu32AtPpc($pagetable_addr+($offset+$i)*4);
    if(!exists $ipkpages{$page}) {
      my @ipks = ($ipk_addr);
      $ipkpages{$page} = \@ipks;
    } else {
      push @{$ipkpages{$page}}, $ipk_addr;
    }
  }
}

sub analyze_sce {
  my $sce_addr = shift;
  return if($sce_addr==0);
  #print sprintf( "SCE at 0x%08x\n", $sce_addr );

  my $pagetable = Dmp_Iu32AtPpc($sce_addr);
  my $pagetable_addr = 0;
  #print sprintf( "abpt at 0x%08x\n", $pagetable );
  $pagetable_addr = Adr_rTableToPpc($pagetable);
  if( $pagetable_addr == 0 ) {
    print "Invalid pagetable\n";
    return;
  }
  #print sprintf( "pagetable at 0x%08x\n", $pagetable_addr );
  my $masklow = Dmp_Iu32AtPpc($sce_addr+4);
  my $maskhigh = Dmp_Iu32AtPpc($sce_addr+8);
  return if ($masklow == 0 && $maskhigh == 0);

  for( my $offset = 0; $offset <32; $offset++ ) {
    my $mask = 1 << $offset;
    my $page;
    if($masklow & $mask) {
      $page = Dmp_Iu32AtPpc( $pagetable_addr + $offset*4 );
      $scepages{$page}++;
      #printf "0x%08x\n", Dmp_Iu32AtPpc( $pagetable_addr + $offset*4 );
    }
    if($maskhigh & $mask) {
      $page = Dmp_Iu32AtPpc( $pagetable_addr + ($offset+32)*4 );
      $scepages{$page}++;
      #printf "0x%08x\n", Dmp_Iu32AtPpc( $pagetable_addr + ($offset+32)*4 );
    }
  }

  my $ipk_addr = Dmp_Iu32AtPpc($sce_addr+0x18);
  while($ipk_addr!=0) {
    printf "IPK at 0x%08x\n", $ipk_addr;
    analyze_ipk($ipk_addr);
    $ipk_addr = Dmp_Iu32AtPpc($ipk_addr);
  }

}

sub analyze_sde {
  my $sde_addr = shift;

  print sprintf( "SDE at 0x%08x\n", $sde_addr );
  my $sde_checkvalue = Dmp_Iu8AtPpc($sde_addr + 0x73);
  if($sde_checkvalue != ord(']')) {
    warn sprintf("SDE 0x%08x checkvalue %d not match\n", $sde_addr, $sde_checkvalue);
    return;
  }
  # Inline SCE
  if(Dmp_Iu8AtPpc($sde_addr + 0x58)!=0xff) { # SDE_InlineSceIndex != RK_SDE_NO_INTERNAL_SCE
    analyze_sce($sde_addr + 0x24);
  }
  for(my $i=0;$i<4;$i++) {
    my $sceext_addr = Dmp_Iu32AtPpc($sde_addr + 0x44 + $i*4);
    next if($sceext_addr==0);
    analyze_sce($sceext_addr);
    analyze_sce($sceext_addr+0x20);
    analyze_sce($sceext_addr+0x40);
    analyze_sce($sceext_addr+0x60);
  }
}

my $sde_count = 0;
for( my $i = 0; $i < $sde_total; $i++ ) {
  my $sde_addr = Dmp_Iu32AtPpc($r5hash + $i*4);
  next if( $sde_addr == 0 );
  $sde_count++;
  analyze_sde($sde_addr);
}

print "SDE count $sde_count\n";

my $key;
print "SCE Duplicated Pages:\n";
for $key (keys %scepages) {
  printf("%08x\n",$key) if($scepages{$key}>1);
}

print "IPK Duplicated Pages:\n";
for $key (keys %ipkpages) {
  if(@{$ipkpages{$key}}>1) {
    printf("0x%08x %d\n",$key,scalar(@{$ipkpages{$key}}));
    for my $ipk_addr (@{$ipkpages{$key}}) {
      printf("0x%08x(state=%d), ",$ipk_addr, Dmp_Iu8AtPpc($ipk_addr+8));
    }
    print "\n";
  }
}
